<?php

namespace Model\Pay;

use Framework\Common;
use Model\Pos\Status;
use Model\Pos\MyException;
use Model\Shop\Payment;
use Model\Helper\CommonHelperSw;
use Model\Ram\User;
use Model\Payment\PaymentException;
use Model\Pos\Transaction;
use Framework\Util\Log;

class Fuioupay
{
    private $insCd;

    private $mchntCd;

    private $privateKeyResource;

    private $publicKeyResource;

    private $url;

    public $fuiouPaymentConfig;

    public function __construct($shopId, $type = Status::ORDER_PAY_FUIOU_WX)
    {
        $fuiouConfig   = Common::getConfigFile('ThirdPaymentConfig', 'Fuiou');
        $this->insCd   = $fuiouConfig['insCd'];
        $this->url     = $fuiouConfig['url'];
        $this->shopId  = $shopId;
        $payConfig     = $this->getPayConfig($this->shopId);
        $this->mchntCd = $payConfig['mchnt_cd'];

        $this->privateKeyResource = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($payConfig['private_key'], 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        if (openssl_pkey_get_private($this->privateKeyResource) == false) {
            throw new MyException(MyException::MERCHANT_PRIVATEKEY_ERR);
        }

        $this->publicKeyResource = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($payConfig['public_key'], 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";

        if (openssl_pkey_get_public($this->publicKeyResource == false)) {
            throw new MyException(MyException::MERCHANT_PUBLICKEY_ERR);
        }
    }

    /**
     * 获取配置信息
     * @param $shopId
     * @return array
     */
    public function getPayConfig($shopId)
    {
        $payConfig = (new Payment($shopId))->getPayConfig('富友');

        return $payConfig;
    }

    /**
     * 条码支付，商户扫用户二维码收款
     *
     * @param string $orderId
     * @param string $authCode
     * @param string $totalAmount
     * @param string $title
     * @param string $orderType
     * @return boolean
     */
    public function microPay($orderId, $authCode, $title, $totalAmount, $orderType)
    {
        $params = [
            'version'        => '1.0',
            'ins_cd'         => $this->insCd,
            'mchnt_cd'       => $this->mchntCd,
            'term_id'        => '88888888',
            'random_str'     => self::createRandomStr(),
            'order_type'     => $orderType,
            'goods_des'      => iconv('utf-8', 'gbk', $title),
            'goods_detail'   => '',
            'addn_inf'       => '',
            'mchnt_order_no' => $orderId,
            'curr_type'      => 'CNY',
            'order_amt'      => strval(intval(bcmul($totalAmount, 100))),
            'term_ip'        => $_SERVER['REMOTE_ADDR'],
            'txn_begin_ts'   => date('YmdHis', time()),
            'goods_tag'      => '',
            'auth_code'      => $authCode,
            'notify_url'     => $this->getCallbackUrl(),
            'sence'          => '1',// 非必填 String 1 支付场景,默认1；1: 条码支付 2: 声波支付
        ];

        $sign           = "addn_inf=" . $params['addn_inf'] . "&auth_code=" . $params['auth_code'] . "&curr_type=" . $params['curr_type'] . "&goods_des=" . $params['goods_des'] . "&goods_detail=" . $params['goods_detail'] . "&goods_tag=" . $params['goods_tag'] . "&ins_cd=" . $params['ins_cd'] . "&mchnt_cd=" . $params['mchnt_cd'] . "&mchnt_order_no=" . $params['mchnt_order_no'] . "&order_amt=" . $params['order_amt'] . "&order_type=" . $params['order_type'] . "&random_str=" . $params['random_str'] . "&sence=" . $params['sence'] . "&term_id=" . $params['term_id'] . "&term_ip=" . $params['term_ip'] . "&txn_begin_ts=" . $params['txn_begin_ts'] . "&version=" . $params['version'];
        $params['sign'] = $this->encrypSign($sign);
        $data           = self::toXml($params);
        $path           = '/micropay';
        $host           = $this->url . $path;
        $responseStr    = $this->postData($host, $data);
        $response       = urldecode($responseStr);

        Log::debug(['params' => $params], 'Fuiou_microPay_request');
        Log::debug(['paramsXML' => $data], 'Fuiou_microPay_request');
        Log::debug(['responseStr' => $responseStr], 'Fuiou_microPay_request');
        Log::debug(['responseUrlDecode' => $response], 'Fuiou_microPay_request');

        $simpleXMLElementObject = simplexml_load_string($response);
        $result                 = json_decode(json_encode($simpleXMLElementObject, JSON_UNESCAPED_SLASHES), true);

        Log::debug(['responsJsonDecode' => $result], 'Fuiou_microPay_request');

        // 下单成功
        $resultData['status'] = 0;
        // 富友交易单号
        $resultData['transactionId'] = isset($result['transaction_id']) ? $result['transaction_id'] : '';
        if ($result['result_code'] == '000000' && $result['result_msg'] == 'SUCCESS') {
            $resultData['status'] = 1;
            return $resultData;
        } else {
            if ($result['result_code'] == '030010') {
                $resultData['status'] = 0;
                return $resultData;
            } else {
                throw new MyException(400, $result['result_msg']);
            }
        }
    }

    /**
     * 主扫统一下单
     *
     * @param string $orderId
     * @param string $totalAmount
     * @param string $title
     * @param string $orderType
     * @return boolean
     */
    public function qrPay($orderId, $totalAmount, $title, $orderType)
    {
        $params                 = [
            'ins_cd'         => $this->insCd,
            'mchnt_cd'       => $this->mchntCd,
            'goods_des'      => iconv('utf-8', 'gbk', $title),
            'order_type'     => $orderType,
            'order_amt'      => strval(intval(bcmul($totalAmount, 100))),
            'notify_url'     => $this->getCallbackUrl(),
            'addn_inf'       => '',
            'curr_type'      => 'CNY',
            'term_id'        => "88888888",
            'goods_detail'   => '',
            'goods_tag'      => '',
            'version'        => '1.0',
            'random_str'     => self::createRandomStr(),
            'mchnt_order_no' => $orderId,
            'term_ip'        => $_SERVER['REMOTE_ADDR'],
            'txn_begin_ts'   => date('YmdHis', time()),
        ];
        $sign                   = "addn_inf=" . $params['addn_inf'] . "&curr_type=" . $params['curr_type'] . "&goods_des=" . $params['goods_des'] . "&goods_detail=" . $params['goods_detail'] . "&goods_tag=" . $params['goods_tag'] . "&ins_cd=" . $params['ins_cd'] . "&mchnt_cd=" . $params['mchnt_cd'] . "&mchnt_order_no=" . $params['mchnt_order_no'] . "&notify_url=" . $params['notify_url'] . "&order_amt=" . $params['order_amt'] . "&order_type=" . $params['order_type'] . "&random_str=" . $params['random_str'] . "&term_id=" . $params['term_id'] . "&term_ip=" . $params['term_ip'] . "&txn_begin_ts=" . $params['txn_begin_ts'] . "&version=" . $params['version'];
        $params['sign']         = $this->encrypSign($sign);
        $data                   = self::toXml($params);
        $path                   = '/preCreate';
        $host                   = $this->url . $path;
        $response               = urldecode($this->postData($host, $data));
        $simpleXMLElementObject = simplexml_load_string($response);
        $result                 = json_decode(json_encode($simpleXMLElementObject, JSON_UNESCAPED_SLASHES), true);
        // Log::debug($result, 'Fuiou_qapay_result_20190809');
        // Log::destroyInstance();
        // 统一下单成功
        if ($result['result_code'] == '000000' && $result['result_msg'] == 'SUCCESS') {
            return $result;
        } else {
            throw new MyException(400, $result['result_msg']);
        }
    }

    /**
     * 退款申请
     *
     * @param string $orderId
     * @param string $refundOrderId
     * @param string $totalAmount
     * @param string $refundAmount
     * @param string $orderType
     * @return boolean
     */
    public function commonRefund($orderId, $refundOrderId, $totalAmount, $refundAmount, $orderType, $operatorId)
    {
        $params = [
            'version'         => '1.0',
            // 必填 String 8 1.0
            'ins_cd'          => $this->insCd,
            // 必填 String 20 机构号,接入机构在富友的唯一代码
            'mchnt_cd'        => $this->mchntCd,
            // 必填 String 15 富友分配的商户号
            'term_id'         => '88888888',
            // 必填 String 8 终端号(没有真实终端号统一填88888888)
            'mchnt_order_no'  => $orderId,
            // 必填 String 30 商户原交易订单号, 商户系统内部的订单号（5到30 个字符、只能包含字母数字,区分大小写)
            'random_str'      => self::createRandomStr(),
            // 必填 String 32 随机字符串，不长于32 位
            'order_type'      => $orderType,
            // 必填 String 20 订单类型:ALIPAY (统一下单、条码支付、服务窗支付), WECHAT(统一下单、条码支付，公众号支付),UNIONPAY,BESTPAY(翼支付)
            'refund_order_no' => $refundOrderId,
            // 必填 String 30 商户退款单号（5 到30 个字符、 只能包含字母数字或者下划线，区分大小写）
            'total_amt'       => strval(intval(bcmul($totalAmount, 100))),
            // 必填 Number 16 总金额
            'refund_amt'      => strval(intval(bcmul($refundAmount, 100))),
            // 必填 Number 16 退款金额
            'operator_id'     => $operatorId,
            // 非必填 String 32 操作员
        ];

        $sign                   = "ins_cd=" . $params['ins_cd'] . "&mchnt_cd=" . $params['mchnt_cd'] . "&mchnt_order_no=" . $params['mchnt_order_no'] . "&operator_id=" . $params['operator_id'] . "&order_type=" . $params['order_type'] . "&random_str=" . $params['random_str'] . "&refund_amt=" . $params['refund_amt'] . "&refund_order_no=" . $params['refund_order_no'] . "&term_id=" . $params['term_id'] . "&total_amt=" . $params['total_amt'] . "&version=" . $params['version'];
        $params['sign']         = $this->encrypSign($sign);
        $data                   = self::toXml($params);
        $path                   = '/commonRefund';
        $host                   = $this->url . $path;
        $response               = urldecode($this->postData($host, $data));
        $simpleXMLElementObject = simplexml_load_string($response);
        $result                 = json_decode(json_encode($simpleXMLElementObject, JSON_UNESCAPED_SLASHES), true);
        // Log::debug($result, 'Fuiou_commonRefund_result_20190809');
        // Log::destroyInstance();
        if ($result['result_code'] == '000000' && $result['result_msg'] == 'SUCCESS') {
            return $result;
        } else {
            throw new MyException(400, $result['result_msg']);
        }
    }

    /**
     * 订单查询
     *
     * @param string $orderId
     * @param string $orderType
     * @return boolean
     */
    public function commonQuery($orderId, $orderType)
    {
        $params = [
            'version'        => '1',
            // 必填 String 8 1
            'ins_cd'         => $this->insCd,
            'mchnt_cd'       => $this->mchntCd,
            'term_id'        => '88888888',
            'order_type'     => $orderType,
            // 必填 String 20 订单类型:ALIPAY (统一下单、条码支付、服务窗支付), WECHAT(统一下单、条码支付，公众号支付,小程序),UNIONPAY,BESTPAY(翼支付)
            'mchnt_order_no' => $orderId,
            'random_str'     => self::createRandomStr(),
        ];

        $sign                   = "ins_cd=" . $params['ins_cd'] . "&mchnt_cd=" . $params['mchnt_cd'] . "&mchnt_order_no=" . $params['mchnt_order_no'] . "&order_type=" . $params['order_type'] . "&random_str=" . $params['random_str'] . "&term_id=" . $params['term_id'] . "&version=" . $params['version'];
        $params['sign']         = $this->encrypSign($sign);
        $data                   = $this->toXml($params);
        $path                   = '/commonQuery';
        $host                   = $this->url . $path;
        $response               = urldecode($this->postData($host, $data));
        $simpleXMLElementObject = simplexml_load_string($response);
        $result                 = json_decode(json_encode($simpleXMLElementObject, JSON_UNESCAPED_SLASHES), true);
        Log::debug($response, 'Fuiou_commonQuery_response_20190910');
        Log::debug($result, 'Fuiou_commonQuery_result_20190910');
        Log::destroyInstance();
        if ($result['result_code'] == '000000' && $result['result_msg'] == 'SUCCESS') {
            return $result;
        } else {
            return false;
        }
    }

    /**
     * 撤销接口（订单支付）
     *
     * @param string $orderId
     * @param string $cancelOrderId
     * @param string $orderType
     * @return boolean
     */
    public function cancelOrder($orderId, $cancelOrderId, $orderType)
    {
        $params = [
            'cancel_order_no' => $cancelOrderId,
            'ins_cd'          => $this->insCd,
            'mchnt_cd'        => $this->mchntCd,
            'mchnt_order_no'  => $orderId,
            'operator_id'     => '',
            'order_type'      => $orderType,
            'random_str'      => self::createRandomStr(),
            'term_id'         => '88888888',
            'version'         => '1.0'
        ];
        \Illuminate\Support\Facades\Log::debug($params, 'Fuiou_cancelOrder_params');

        $sign           = "cancel_order_no=" . $params['cancel_order_no'] . "&ins_cd=" . $params['ins_cd'] . "&mchnt_cd=" . $params['mchnt_cd'] . "&mchnt_order_no=" . $params['mchnt_order_no'] . "&operator_id=" . $params['operator_id'] . "&order_type=" . $params['order_type'] . "&random_str=" . $params['random_str'] . "&term_id=" . $params['term_id'] . "&version=" . $params['version'];
        $params['sign'] = $this->encrypSign($sign);
        $data           = self::toXml($params);
        Log::debug($data, 'Fuiou_cancelOrder_data');
        $path                   = '/cancelorder';
        $host                   = $this->url . $path;
        $responseStr            = $this->postData($host, $data);
        $response               = urldecode($responseStr);
        $simpleXMLElementObject = simplexml_load_string($response);
        $result                 = json_decode(json_encode($simpleXMLElementObject, JSON_UNESCAPED_SLASHES), true);
        Log::debug($responseStr, 'Fuiou_cancelOrder_responseStr');
        Log::debug($response, 'Fuiou_cancelOrder_response');
        Log::debug($result, 'Fuiou_cancelOrder_result');
        Log::destroyInstance();
        if (!empty($result) && $result['result_code'] == '000000') {
            return true;
        }

        return false;
    }

    /**
     * 扫码枪支付（轮询查询）
     *
     * @param $shopId
     * @param $serialId
     * @param $orderType
     * @return int|boolean
     */
    public function barQuery($shopId, $serialId, $orderType)
    {
        // 查询交易记录
        $transactionInfo = (new Transaction($shopId))->getTransaction($serialId);
        // Log::debug($transactionInfo, 'Fuiou_barQuery_ask_20190809');
        // Log::destroyInstance();
        $result = $this->commonQuery($serialId, $orderType);
        $status = 0;
        switch ($result['result_msg']) {
            case 'SUCCESS':
                $status = 1;
                break;
            default:
                $status = false;
                break;
        }

        return $status;
    }

    /**
     * 回调返回数据
     * @return string
     */
    public static function callbackReturn()
    {
        return json_encode(["result_code" => "000000", "result_msg" => "SUCCESS"]);
    }

    /**
     * 请求数据
     *
     * @param string $url
     * @param array|string $postData
     * @return string
     */
    public function postData($url, $postData)
    {
        if (is_array($postData)) {
            $postData = json_encode($postData);
        }

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, "$url");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);

        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);

        // 设定为不验证证书和HOST
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        // 执行并获取url地址内容
        $data = curl_exec($ch);

        curl_close($ch);

        return $data;
    }

    /**
     * 加密签名数据
     *
     * @param string $data
     * @return string
     */
    public function encrypSign($data)
    {
        $privateKey = openssl_pkey_get_private($this->privateKeyResource);
        openssl_sign($data, $sign, $privateKey, OPENSSL_ALGO_MD5);
        return base64_encode($sign);
    }

    /**
     * 转成XML格式
     *
     * @param $params
     * @return string
     */
    public static function toXml($params)
    {
        $result = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><xml>";
        foreach ($params as $k => $v) {
            $result .= "<" . $k . ">" . $v . "</" . $k . ">";
        }
        $result .= "</xml>";

        return "req=" . urlencode(urlencode($result));
    }

    /**
     * 转义xml为数组
     * @param $response
     * @return mixed
     * @throws MyException
     */
    public static function fromXml($response)
    {
        $callbackData           = urldecode(urldecode(substr($response, 4)));
        $simpleXMLElementObject = simplexml_load_string($callbackData);

        return json_decode(json_encode($simpleXMLElementObject, JSON_UNESCAPED_UNICODE), true);
    }

    /**
     * 产生随机字符串，不长于32位
     * @param int $length
     * @return string
     */
    public static function createRandomStr($length = 32)
    {
        $string = "abcdefghijklmnopqrstuvwxyz0123456789";
        $result = '';
        for ($i = 0; $i < $length; $i++) {
            $result .= substr($string, mt_rand(0, strlen($string) - 1), 1);
        }

        return $result;
    }

    /**
     * 获取回调地址
     *
     * @return string
     */
    public function getCallbackUrl()
    {
        return CommonHelperSw::getRequestUrl() . '/Pos/Callback/Fuiou';
    }
}
